package com.sp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sp.exception.BusinessException;
import com.sp.mapper.BillIncompleteMapper;
import com.sp.mapper.BillMapper;
import com.sp.model.dto.BillDTO;
import com.sp.model.entity.Bill;
import com.sp.model.entity.BillIncomplete;
import com.sp.model.entity.User;
import com.sp.service.BillService;
import com.sp.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.sp.constant.BillMessageConstant.*;

/**
 * @author tong
 * @date 2024/3/16 9:44
 */
@Service
public class BillServiceImpl implements BillService {

    private final String PAID = "paid";
    private final String UNPAID = "unpaid";

    @Autowired
    private BillIncompleteMapper billIncompleteMapper;

    @Autowired
    private BillMapper billMapper;

    @Autowired
    private UserService userService;

    @Value("${bill.price}")
    private double price;

    @Override
    public BillIncomplete selectBillIncompleteByCarNumber(String carNumber) {
        return billIncompleteMapper.selectByCarNumber(carNumber);
    }

    @Transactional
    @Override
    public boolean addBillIncompleteByCarNumber(BillDTO billDTO) {
        if (billDTO == null || StrUtil.isBlank(billDTO.getCarNumber())) {
            throw new BusinessException(ADD_BILL_FAIL + "：" + PARAM_ERROR);
        }

        // 没出去不能新增
        BillIncomplete b = billIncompleteMapper.selectByCarNumber(billDTO.getCarNumber());
        if (b != null) {
            throw new BusinessException(ADD_BILL_FAIL + "：该车辆存在未完成的账单");
        }

        Bill bill = BeanUtil.copyProperties(billDTO, Bill.class);

        User user = userService.selectByCarNumber(bill.getCarNumber());
        if (user == null) {
            throw new BusinessException(ADD_BILL_FAIL + "：用户不存在");
        }

        bill.setUserId(user.getId());
        bill.setInTime(LocalDateTime.now());
        bill.setStatus(UNPAID);
        bill.setCost(0F);

        if (billMapper.insert(bill) != 1) {
            throw new BusinessException(ADD_BILL_FAIL);
        }

        BillIncomplete billIncomplete = BeanUtil.copyProperties(bill, BillIncomplete.class);
        billIncomplete.setId(null);
        billIncomplete.setBillId(bill.getId());

        return billIncompleteMapper.insert(billIncomplete) == 1;
    }

    @Transactional
    @Override
    public boolean addBillCompleteByCarNumber(BillDTO billDTO) {
        if (billDTO == null || StrUtil.isBlank(billDTO.getCarNumber())) {
            throw new BusinessException(UPDATE_BILL_FAIL + "：" + PARAM_ERROR);
        }

        BillIncomplete billIncomplete = billIncompleteMapper.selectByCarNumber(billDTO.getCarNumber());
        if (billIncomplete == null) {
            throw new BusinessException(UPDATE_BILL_FAIL + "：该车辆不存在未完成账单");
        }

        Bill bill = BeanUtil.copyProperties(billIncomplete, Bill.class);
        bill.setId(billIncomplete.getBillId());
        bill.setOutTime(LocalDateTime.now());
        // 计算金额
        bill.setCost(this.calculateCost(bill));
        // todo 生成支付请求，支付成功后再进行后续操作
        bill.setStatus(PAID);

        // 删除未完成的记录
        if (billIncompleteMapper.deleteById(billIncomplete.getId()) != 1) {
            throw new BusinessException(UPDATE_BILL_FAIL);
        }

        // 更新未完成到完成
        return billMapper.updateById(bill) == 1;
    }

    @Transactional
    @Override
    public boolean updateBillById(BillDTO billDTO) {
        if (billDTO == null || billDTO.getId() == null) {
            throw new BusinessException(UPDATE_BILL_FAIL + "：" + PARAM_ERROR);
        }

        Integer id = billDTO.getId();
        // 查询数据库中原账单数据
        Bill b = billMapper.selectByIdWithUser(id);
        if (b == null) {
            throw new BusinessException(UPDATE_BILL_FAIL + "：该账单不存在");
        }

        String bStatus = b.getStatus();
        if (PAID.equals(bStatus)) {
            // 已支付，不能修改
            throw new BusinessException(UPDATE_BILL_FAIL + "：订单已完成，不能进行修改");
        }

        // 未支付，判断设置的时间是否合理
        LocalDateTime bCreateTime = b.getCreateTime();
        LocalDateTime bInTime = b.getInTime();
        LocalDateTime inTime = billDTO.getInTime();
        LocalDateTime outTime = billDTO.getOutTime();
        //该方法发现错误，直接抛出异常
        checkTime(bCreateTime, bInTime, inTime, outTime);

        Bill bill = BeanUtil.copyProperties(billDTO, Bill.class);

        // 是否需要删除未完成表中的账单数据
        boolean flag = false;
        if (UNPAID.equals(bStatus) && outTime != null) {
            // 离开时间不为空，即要完成该账单
            bill.setCost(this.calculateCost(bill));
            // todo 完成要在支付后设置
            bill.setStatus(PAID);

            flag = true;
        } else {
            bill.setCost(null);
            bill.setStatus(null);
        }

        // 是否修改账单的车牌号
        String carNumber = billDTO.getCarNumber();
        String bCarNumber = b.getCarNumber();
        if (StrUtil.isNotBlank(carNumber) && !bCarNumber.equals(carNumber)) {
            User user = userService.selectByCarNumber(carNumber);
            if (user == null) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：用户不存在");
            }

            bill.setUserId(user.getId());
        }

        if (billMapper.updateById(bill) != 1) {
            throw new BusinessException(UPDATE_BILL_FAIL);
        }

        if (flag) {
            // 修改订单为完成，未完成订单删除
            if (billIncompleteMapper.deleteByBillId(Collections.singletonList(bill.getId())) != 1) {
                throw new BusinessException(UPDATE_BILL_FAIL);
            }
            return true;
        } else {
            // 修改订单其它信息，修改未完成订单
            BillIncomplete billIncomplete = new BillIncomplete();
            BeanUtils.copyProperties(bill, billIncomplete);
            billIncomplete.setId(null);
            billIncomplete.setBillId(bill.getId());
            LambdaUpdateWrapper<BillIncomplete> updateWrapper = new LambdaUpdateWrapper<BillIncomplete>()
                    .eq(BillIncomplete::getBillId, billIncomplete.getBillId());

            if (billIncompleteMapper.update(billIncomplete, updateWrapper) != 1) {
                throw new BusinessException(UPDATE_BILL_FAIL);
            }
            return true;
        }
    }

    /**
     * 判断修改时间是否合理<br/>
     * 该方法发现错误，直接抛出异常
     *
     * @param bCreateTime
     * @param bInTime
     * @param inTime
     * @param outTime
     */
    private void checkTime(LocalDateTime bCreateTime, LocalDateTime bInTime, LocalDateTime inTime, LocalDateTime outTime) {
        if (inTime != null && outTime != null) {
            if (inTime.isBefore(bCreateTime)) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：进入时间不能早于创建时间");
            }
            if (inTime.isAfter(outTime)) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：进入时间不能晚于离开时间");
            }
            if (outTime.isAfter(LocalDateTime.now())) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：离开时间不能晚于当前时间");
            }
        } else if (inTime != null) {
            if (inTime.isBefore(bCreateTime)) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：进入时间不能早于创建时间");
            }
            if (inTime.isAfter(LocalDateTime.now())) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：进入时间不能晚于当前时间");
            }
        } else if (outTime != null) {
            if (outTime.isBefore(bInTime)) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：离开时间不能早于进入时间");
            }
            if (outTime.isAfter(LocalDateTime.now())) {
                throw new BusinessException(UPDATE_BILL_FAIL + "：离开时间不能晚于当前时间");
            }
        }
    }

    @Transactional
    @Override
    public boolean deleteBillById(List<Integer> ids) {
        if (ids == null || ids.size() == 0) {
            throw new BusinessException(DELETE_BILL_FAIL + "：" + PARAM_ERROR);
        }

        int size = ids.size();
        int num1 = billMapper.deleteBatchIds(ids);
        if (num1 != size) {
            throw new BusinessException(DELETE_BILL_FAIL);
        }

        int num2 = billIncompleteMapper.deleteByBillId(ids);
        if (num2 != size) {
            throw new BusinessException(DELETE_BILL_FAIL);
        }
        return true;
    }

    @Override
    public IPage<Bill> page(Integer currentPage, Integer pageSize, BillDTO billDTO) {
        if (currentPage == null) {
            currentPage = 1;
        }

        if (pageSize == null) {
            pageSize = 10;
        }

        //条件查询
        QueryWrapper<Bill> queryWrapper = new QueryWrapper<>();
        if (billDTO != null) {
            queryWrapper.like(StrUtil.isNotBlank(billDTO.getName()), "u.`name`", billDTO.getName())
                    .eq(StrUtil.isNotBlank(billDTO.getSex()), "u.`sex`", billDTO.getSex())
                    .like(StrUtil.isNotBlank(billDTO.getCarNumber()), "u.`car_number`", billDTO.getCarNumber())
                    .like(StrUtil.isNotBlank(billDTO.getPhone()), "u.`phone`", billDTO.getPhone())
                    .gt(null != billDTO.getInTimeStart(), "b.`in_time`", billDTO.getInTimeStart())
                    .lt(null != billDTO.getInTimeEnd(), "b.`in_time`", billDTO.getInTimeEnd())
                    .gt(null != billDTO.getOutTimeStart(), "b.`out_time`", billDTO.getOutTimeStart())
                    .lt(null != billDTO.getOutTimeEnd(), "b.`out_time`", billDTO.getOutTimeEnd())
                    .eq(StrUtil.isNotBlank(billDTO.getStatus()), "b.`status`", billDTO.getStatus())
                    .gt(null != billDTO.getCreateTimeStart(), "b.`create_time`", billDTO.getCreateTimeStart())
                    .lt(null != billDTO.getCreateTimeEnd(), "b.`create_time`", billDTO.getCreateTimeEnd());
        }
        IPage<Bill> page = new Page<>(currentPage, pageSize);
        return billMapper.page(page, queryWrapper);
    }

    /**
     * 根据进入和离开时间计算停车费用<br/>
     * 计费规则：半小时内免费，1小时单价${bill.price}
     *
     * @param bill
     * @return
     */
    private float calculateCost(Bill bill) {
        LocalDateTime inTime = bill.getInTime();
        LocalDateTime outTime = bill.getOutTime();
        // 计算停车时长
        Duration duration = Duration.between(inTime, outTime);
        // 转换为分钟
        long minutes = duration.toMinutes() - 30;
        if (minutes <= 0) {
            return 0F;
        }
        double hours = minutes / 60.0;
        // 向上取整，newScale要返回的BigDecimal值的小数位数，roundingMode要应用的舍入模式
        BigDecimal roundHours = new BigDecimal(hours).setScale(0, RoundingMode.UP);
        // 乘以单价
        return roundHours.multiply(BigDecimal.valueOf(price)).floatValue();
    }

    @Override
    public List<Map<String, String>> getMoneyData(LocalDateTime createTime, Integer range) {
        int mid = getMid(createTime, range);
        LocalDateTime start = createTime.minusDays(mid);
        LocalDateTime end = createTime.plusDays(mid);
        return billMapper.getMoneyData(start, end);
    }

    @Override
    public List<Map<String, String>> getAmountData(LocalDateTime createTime, Integer range, String status) {
        int mid = getMid(createTime, range);
        LocalDateTime start = createTime.minusDays(mid);
        LocalDateTime end = createTime.plusDays(mid);
        return billMapper.getAmountData(start, end, status);
    }

    /**
     * 校验参数并计算日期的一半长度
     *
     * @param time
     * @param range
     * @return
     */
    private int getMid(LocalDateTime time, Integer range) {
        if (time == null || range == null || range <= 0) {
            throw new BusinessException(SELECT_BILL_FAIL + "：" + PARAM_ERROR);
        }
        return range / 2;
    }

    @Override
    public BillIncomplete selectBillIncompleteByUserId(Integer userId) {
        return billIncompleteMapper.selectByUserId(userId);
    }

}
